import os
from typing import Tuple, List, Dict

import requests
from PIL import Image, ImageDraw, ImageFont

# 难度名称映射(数字->文字)
difficulty_mapping_up: List[str] = [
    'BASIC',
    'ADVANCED',
    'EXPERT',
    'MASTER',
    'ULTIMA',
    'WORLD\'S END'
]

# 难度对应颜色
color: Dict[str, Tuple[int, int, int]] = {
    'master': (187, 51, 238),
    'expert': (238, 67, 102),
    'advanced': (254, 170, 0),
    'ultima': (0, 0, 0),
    'basic': (102, 221, 17),
}

# 难度映射(数字->英文)
difficulty_mapping: Dict[str, str] = {
    "0": "basic",
    "1": "advanced",
    "2": "expert",
    "3": "master",
    "4": "ultima",
    "5": "worldsend"
}


def get_title(song: str, width: int) -> str:
    """
    获取适合显示宽度的歌曲标题
    
    如果标题太长会截断并添加省略号
    
    Args:
        song: 原始歌曲标题
        width: 可用显示宽度
        
    Returns:
        str: 处理后的歌曲标题
    """
    font_title = ImageFont.truetype('assets/fonts/MSYHBD.TTC', 30)
    title = song
    length = font_title.getlength(title)

    if length > width - 188:
        while True:
            title = title[:-1]
            length = font_title.getlength(title)
            if length <= width - 188 - 8:
                title += '...'
                break

    return title


def get_image(jacket: str) -> Image.Image:
    """
    获取歌曲封面图片
    
    优先从本地缓存获取,如果没有则从网络下载并缓存
    
    Args:
        jacket: 封面图片ID
        
    Returns:
        Image: PIL Image对象
    """
    # 确保图片缓存目录存在
    if not os.path.exists('assets/image'):
        os.makedirs('assets/image')

    cache_path = f'assets/image/{jacket}_thumbnail.webp'

    # 优先使用缓存
    if os.path.exists(cache_path):
        return Image.open(cache_path)

    # 从网络下载
    url = f'https://oss.bemanicn.com/SDHD/cover/{jacket}.webp-thumbnail'
    image = Image.open(requests.get(url, stream=True).raw)
    image.save(cache_path)
    return image


def create_rating_image(number: float) -> Image.Image:
    """
    创建Rating值图片
    
    Args:
        number: Rating值(乘以100后的整数)
        
    Returns:
        Image: PIL Image对象
    """
    # 确定Rating等级
    level = _get_rating_level(number)

    # 格式化数字
    formatted_number = _format_rating_number(number)

    # 获取数字图片列表
    image_files = _get_rating_image_files(formatted_number, level)

    # 合成最终图片
    return _compose_rating_image(image_files)


def draw_rounded_rectangle(draw: ImageDraw, pos: Tuple[int, int, int, int],
                           corner_radius: int, fill: Tuple[int, int, int] = None):
    """
    绘制圆角矩形
    
    Args:
        draw: PIL ImageDraw对象
        pos: 矩形位置(x1, y1, x2, y2)
        corner_radius: 圆角半径
        fill: 填充颜色
    """
    x1, y1, x2, y2 = pos

    # 绘制主体矩形
    draw.rectangle([(x1, y1 + corner_radius), (x2, y2 - corner_radius)], fill=fill)
    draw.rectangle([(x1 + corner_radius, y1), (x2 - corner_radius, y2)], fill=fill)

    # 绘制四个圆角
    draw.pieslice([x1, y1, x1 + corner_radius * 2, y1 + corner_radius * 2],
                  180, 270, fill=fill)
    draw.pieslice([x2 - corner_radius * 2, y1, x2, y1 + corner_radius * 2],
                  270, 360, fill=fill)
    draw.pieslice([x2 - corner_radius * 2, y2 - corner_radius * 2, x2, y2],
                  0, 90, fill=fill)
    draw.pieslice([x1, y2 - corner_radius * 2, x1 + corner_radius * 2, y2],
                  90, 180, fill=fill)


def create_base(width: int, length: int) -> Tuple[Image.Image, ImageDraw.ImageDraw]:
    """
    创建基础图片
    
    创建指定大小的白色背景图片,并添加渐变色背景
    
    Args:
        width: 图片宽度
        length: 图片长度
        
    Returns:
        tuple: (PIL Image对象, PIL ImageDraw对象)
    """
    # 创建白色背景图片
    pic = Image.new('RGB', (width, length), (255, 255, 255))
    draw = ImageDraw.Draw(pic)

    # 生成渐变色
    warm_colors = _generate_gradient((255, 0, 0), (255, 255, 255), 24)

    # 绘制渐变背景
    for i in range(24):
        draw.polygon([(i * 147, 0), ((i + 1) * 147, 0), (2480, (i + 1) * 147), (2480, i * 147)],
                     fill=warm_colors[-i])

    for i in range(24):
        draw.polygon([(0, i * 147), (0, (i + 1) * 147), ((i + 1) * 147, 2480), (i * 147, 2480)],
                     fill=warm_colors[23 - i - (-1) ** i])

    return pic, draw


def truncate_two_decimal_places(number: float) -> float:
    """
    保留两位小数(截断而非四舍五入)
    
    Args:
        number: 原始数字
        
    Returns:
        float: 处理后的数字
    """
    if int(number) == 0:
        return 0

    str_number = str(number + 0.00000002)
    decimal_index = str_number.find('.')
    if decimal_index != -1:
        str_number = str_number[:decimal_index + 3]
    return float(str_number)


def _get_rating_level(number: float) -> str:
    """
    获取Rating等级
    
    Args:
        number: Rating值
        
    Returns:
        str: Rating等级名称
    """
    if number <= 399:
        return 'green'
    elif number <= 699:
        return 'orange'
    elif number <= 999:
        return 'red'
    elif number <= 1199:
        return 'purple'
    elif number <= 1324:
        return 'bronze'
    elif number <= 1449:
        return 'silver'
    elif number <= 1524:
        return 'gold'
    elif number <= 1599:
        return 'platinum'
    else:
        return 'rainbow'


def _format_rating_number(number: float) -> str:
    """
    格式化Rating数字为xx.xx格式
    
    Args:
        number: Rating值
        
    Returns:
        str: 格式化后的字符串
    """
    return f"{number / 100:.2f}"


def _get_rating_image_files(formatted_number: str, level: str) -> List[str]:
    """
    获取Rating数字图片文件名列表
    
    Args:
        formatted_number: 格式化后的Rating数字
        level: Rating等级
        
    Returns:
        list: 图片文件名列表
    """
    integer_part, decimal_part = formatted_number.split('.')
    image_files = []

    # 添加整数部分
    for digit in integer_part:
        image_files.append(f'rating_{level}_{int(digit):02d}.png')

    # 添加小数点
    image_files.append(f'rating_{level}_comma.png')

    # 添加小数部分
    for digit in decimal_part:
        image_files.append(f'rating_{level}_{int(digit):02d}.png')

    return image_files


def _compose_rating_image(image_files: List[str]) -> Image.Image:
    """
    合成Rating图片
    
    Args:
        image_files: 图片文件名列表
        
    Returns:
        Image: 合成后的PIL Image对象
    """
    rating_dir = 'assets/rating'

    # 加载所有图片
    images = [Image.open(os.path.join(rating_dir, file)).convert("RGBA")
              for file in image_files]

    # 计算总宽度和最大高度
    total_width = sum(img.width for img in images)
    max_height = max(img.height for img in images)

    # 创建新图片
    result_image = Image.new('RGBA', (total_width, max_height), (255, 255, 255, 255))

    # 拼接图片
    current_width = 0
    for i, img in enumerate(images):
        # 小数点位置稍低,数字居中
        offset_y = (max_height - img.height) \
            if image_files[i].endswith('_comma.png') \
            else (max_height - img.height) // 2
        result_image.paste(img, (current_width, offset_y), img)
        current_width += img.width

    return result_image


def _generate_gradient(start_rgb: Tuple[int, int, int],
                       end_rgb: Tuple[int, int, int],
                       steps: int) -> List[Tuple[int, int, int]]:
    """
    生成RGB渐变色列表
    
    Args:
        start_rgb: 起始RGB颜色
        end_rgb: 结束RGB颜色
        steps: 渐变步数
        
    Returns:
        list: RGB颜色元组列表
    """
    r_step = (end_rgb[0] - start_rgb[0]) / steps
    g_step = (end_rgb[1] - start_rgb[1]) / steps
    b_step = (end_rgb[2] - start_rgb[2]) / steps

    gradient = []
    for i in range(steps):
        r = int(start_rgb[0] + (r_step * i))
        g = int(start_rgb[1] + (g_step * i))
        b = int(start_rgb[2] + (b_step * i))
        gradient.append((r, g, b))

    return gradient
